﻿// File: CsoGateway.Collections.NameValueCollection.js
// Version: 0.7.1.0
// Author: Pascal Dufresne
// Date: 2009-03-13
// Last update: 2009-05-25
// http://csogateway.codeplex.com
// http://csogateway.metaobjects.ca
// Copyright (C) 2010 Pascal Dufresne

/*
 * Register CsoGateway.Collections namespace. Import CsoGateway.System namespace.
 */
Type.registerNamespace("CsoGateway.Collections");
ImportNamespace(CsoGateway.System);

/*
 * CsoGateway.Collections.NameValueCollection represents a collection of associated String
 * keys and String values that can be accessed either with the key or with the index. 
 * It's interface is meant to resemble that of the .NET class System.Collections.NameValueCollection.
 * It's internal implementation is a CsoGateway.Collections.Dictionary.
 * It differs from a CsoGateway.Collections.Dictionary in the following ways:		
 *		- Many valus can be associated with a key
 *		- Keys and values can be of type String only
 *		- null is accepted as a value for the key
 *
 * The respect of its interface is strictly enforced. All method parameters
 * must be specified, they must be of the right type and within an acceptable
 * range or an exception is thrown.
 *
 * This class uses the MicrosoftAjax.js Type extensions.
 * It is build on top of the MicrosoftAjax.js library and cannot function without it.
 *
 * Contructor                       'System.Collections.Specialized.NameValueCollection' equivalent
 * ----------                       -----------------------------------------------------------
 * NameValueCollection()            NameValueCollection()
 * NameValueCollection              NameValueCollection(NameValueCollection)
 * (key1, valueArray1, key2, ...)				
 *
 *
 * Methods                          'System.Collections.Specialized.NameValueCollection' equivalent
 * ----------                       -----------------------------------------------------------
 * Add(String,String)               Add(System.String, System.String)
 * Clear()                          Clear()
 * Get(String/Number)               Get(System.String), Get(System.Int32)								
 * GetKey(Number)                   GetKey(System.Int32)
 * GetValues(String/Number)         GetValues(System.String),GetValues(System.Int32)
 * HasKeys()                        HasKeys()
 * Remove(String)                   Remove(System.String)
 * Set(String, String)              Set (System.String, System.String)
 * 
 */

/*
 * Creates a new CsoGateway.Collections.NameValueCollection.
 * There are 2 ways to create a Dictionary object corresponding to 2 different constructor:
 * - NameValueCollection()
 *		Create an empty NameValueCollection. The internal Dictionary will be created with 32 buckets.
 * - NameValueCollection(key1, arrayOfValue1, key2, arrayOfValue2, ... keyN, arrayOfValueN)
 *		The number of arguments must be even or an exception is thrown. A new NameValue collection is created
 *		with N key/values pairs. Since, a NameValueCollection allows for many values per key, the value arguments
 *		are not of type String but of type Array. These arrays can only contain String objects.
 *		Ex: NameValueCollection x = new NameValueCollection("color", ["red", "white", "bleu"], "brand", ["BMW", "Mazda"]);
 */
ImportType(CsoGateway.Collections.Dictionary);


CsoGateway.Collections.NameValueCollection = function NameValueCollection()
{
	this.NullKey = '_CsoGateway.Collections.NameValueCollection_null_key';

	if(arguments.length == 0)
	{
		this.innerDictionary = new Dictionary()
	}
	else if(arguments.length%2 == 0)
	{
		this.innerDictionary = new Dictionary(arguments.length);
		for(var i=0; i<arguments.length; i+=2)
		{
			if(arguments[i] != null)
				AssertArgumentType(arguments[i], String, 'key_' + (i/2) + ' in CsoGateway.Collections.NameValueCollection constructor');
			AssertArgumentType(arguments[i+1], Array, 'valueArray_' + (i/2) + ' in CsoGateway.Collections.NameValueCollection constructor');
			
			for(var j=0; j<arguments[i+1].length; ++j)
			{
				if(arguments[i+1][j]!=null)
					AssertArgumentType(arguments[i+1][j], String, "valueArray" + (i/2).toString() + "[" + j + "] in CsoGateway.Collections.NameValueCollection constructor");
			}
			
			if(arguments[i] != null)
			{
				if(!((arguments[i+1].length == 1) && (arguments[i+1][0] == null))) // Do not add if only value is null
					this.innerDictionary.setItem(arguments[i], arguments[i+1].slice());
			}
			else
			{
				if(!((arguments[i+1].length == 1) && (arguments[i+1][0] == null))) // Do not add if only value is null
					this.innerDictionary.setItem(this.NullKey, arguments[i+1].slice());
			}
		}
	}
	else
	{
		throw new Error('CsoGateway.Collections.NameValueCollection: The number of arguments passed to constructor must be even.');
	}
}

/*
 * Adds a new value to the specified key. If the key is already in the NameValueCollection, the value is added with
 * the values already associated with that key. The key -and- value parameter can be null.
 */
CsoGateway.Collections.NameValueCollection.prototype.Add =  function(key, value)
{
	var normalizedKey = ((key==null) ? this.NullKey : key);
	
	AssertArgumentNotUndefined(normalizedKey, 'key in CsoGateway.Collections.NameValueCollection.prototype.Add');
	AssertArgumentNotUndefined(value, 'value in CsoGateway.Collections.NameValueCollection.prototype.Add');
	
	
	AssertArgumentType(normalizedKey, String, 'key in CsoGateway.Collections.NameValueCollection.prototype.Add');
	AssertArgumentType(value, String, 'value in CsoGateway.Collections.NameValueCollection.prototype.Add');

	if(this.innerDictionary.ContainsKey(normalizedKey))
	{
		if(value !== null) // Do not add null value
		{
			this.innerDictionary.getItem(normalizedKey).push(value);
		}
	}
	else
	{
		if(value !== null) // Do not add null value
		{
			this.innerDictionary.setItem(normalizedKey, [value]);
		}
	}
}

/* 
 * Removes all key/values pairs from the NameValueCollections.
 *
 */
CsoGateway.Collections.NameValueCollection.prototype.Clear =  function()
{
	this.innerDictionary.Clear();
}

/* 
 * Gets the values of a specified entry in the NameValueCollection combined into one comma-separated list.
 * If the parameter 'keyOrIndex' is of type String, it will be considered a key, if it is of type Number,
 * it will be considered an index.
 *
 */
CsoGateway.Collections.NameValueCollection.prototype.Get =  function(keyOrIndex)
{
	var values;
	if(keyOrIndex === null)
	{
		keyOrIndex = this.NullKey;
	}
	
	if(String.isInstanceOfType(keyOrIndex))
	{
		values = this.innerDictionary.getItem(keyOrIndex);
		
	}
	else
	{
		AssertArgumentIsNumberInRange(keyOrIndex, 0, this.innerDictionary.Count()-1, 'keyOrIndex');
		
		values = this.innerDictionary.getItem(this.innerDictionary.Keys[keyOrIndex]);		
	}
	
	if(values == null || values.length == 0)
		return null;
	else
		return values.toString();
}

/*
 * Gets the key at the specified index of the NameValueCollection.
 */
CsoGateway.Collections.NameValueCollection.prototype.GetKey =  function(index)
{
	AssertArgumentIsNumberInRange(index, 0, this.innerDictionary.Count()-1, 'index in CsoGateway.Collections.NameValueCollection.prototype.GetKey');
	
	if (this.innerDictionary.Keys()[index] == this.NullKey)
		return null;
	else
		return this.innerDictionary.Keys()[index];
}

/*
 * Gets the values of a specified entry in the NameValueCollection.
 * If the parameter 'keyOrIndex' is of type String, it will be considered an index, if it is of type Number,
 * it will be considered an index.
 */
CsoGateway.Collections.NameValueCollection.prototype.GetValues =  function(keyOrIndex)
{
	var values;
	if(keyOrIndex === null)
	{
		keyOrIndex = this.NullKey;
	}
	
	if(String.isInstanceOfType(keyOrIndex))
	{
		values = this.innerDictionary.getItem(keyOrIndex);
		
	}
	else
	{
		AssertArgumentIsNumberInRange(keyOrIndex, 0, this.innerDictionary.Count()-1, 'keyOrIndex in CsoGateway.Collections.NameValueCollection.prototype.GetValues');
		
		values = this.innerDictionary.getItem(this.innerDictionary.Keys()[keyOrIndex]);		
	}
	
	if(values == null || values.length == 0)
		return null;
	else
		return values.slice();
}

/*
 * Gets a value indicating whether the NameValueCollection contains keys that are not a null reference
 */
CsoGateway.Collections.NameValueCollection.prototype.HasKeys =  function()
{
	return this.innerDictionary.Keys().length > 0;
}

/*
 * Removes the entries with the specified key.
 */
CsoGateway.Collections.NameValueCollection.prototype.Remove =  function(key)
{
	if(key === null)
		this.innerDictionary.Remove(this.NullKey);
	else
		this.innerDictionary.Remove(key);
}

/*
 * Sets the value for the specified key. If the key already exists in the NameValueCollection, the values associated
 * with the key are eraser and replaced by the new value. To append a value to the already existing list of values
 * use the funtion Add(key, value).
 */
CsoGateway.Collections.NameValueCollection.prototype.Set =  function(key, value)
{
	AssertArgumentNotUndefined(key, 'key in CsoGateway.Collections.NameValueCollection.prototype.Set');
	
	if(key !== null)
		AssertArgumentType(key, String, 'key in CsoGateway.Collections.NameValueCollection.prototype.Set');
	if(value !== null)
		AssertArgumentType(value, String, 'value in CsoGateway.Collections.NameValueCollection.prototype.Set');
	
	if(key === null)
	{
		if(value == null)
			this.innerDictionary.Remove(this.NullKey);
		else
			this.innerDictionary.setItem(this.NullKey, [value]);
	}
	else
	{
		if(value == null)
			this.innerDictionary.Remove(key);
		else
			this.innerDictionary.setItem(key, [value]);
	}
}

/*
 * Register the class CsoGateway.Collections.NameValueCollection
 */
CsoGateway.Collections.NameValueCollection.registerClass('CsoGateway.Collections.NameValueCollection', CsoNative, Sys.IDisposable);

/*
 * End of CsoGateway.Collections.NameValueCollection definition
 */